/**
 * @file
 * Main application header file.
 *
 * Kisa provides spell checking as you type and displays the result in a small
 * window on your Desktop.
 *
 * Copyright (c) 2009 by Pete Black <theblackpeter@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * @author Pete Black <theblackpeter@gmail.com>
 */
#ifndef KISA_H
#define KISA_H

#include <QApplication>
#include <QSystemTrayIcon>

class QAction;
class QCompleter;
class QProcess;

class KisaLib;
class KisaWidget;
class KisaSettings;
class KisaThread;

/**
 * @class Kisa
 * The Kisa class provides the main application, using a reimplementation of
 * QApplication. It manages the GUI application's control flow and main
 * settings. All the algorithms, logic and data structures that constitute the
 * core of the application are kept here.
 *
 * It also contains the main event loop, which is the final destination for all
 * other events from the window system and other sources.
 *
 * This is not the main window or worker thread. See KisaWidget and KisaThread
 * respectively. External API function calls, such as X11 and Aspell are made
 * through the library, KisaLib, which is instantiated here.
 *
 * @image html kisa_suggestion_popup.png "Kisa main widget window and pop-up"
 *
 * @author Pete Black <theblackpeter@gmail.com>
 */
class Kisa: public QApplication {
  Q_OBJECT

  public:
  /**
   * Creates a new application instance using @p argc command line arguments
   * given by @p argv.
   *
   * Does all object instantiation and initialization, for example the library,
   * KisaLib, the worker thread, KisaThread, the main window KisaWidget,
   * the settings window, KisaSettings and many more.
   *
   * @note Qt has a set of default arguments, these are not processes here.
   *
   * @param argc the number of command line arguments
   * @param argv the command line arguments
   *
   * @see showCommandLineHelp
   */
  Kisa(int& argc, char** argv);

  /**
   * Destroys the application.
   */
  virtual ~Kisa();

  /**
   * Checks if the selected dictionary has been used before. If so @c true is
   * returned, otherwise @c false.
   *
   * @param index the position of the dictionary in the dictionary list or
   * combo box in the settings window
   * @return @c true if the dictionary has been used, otherwise @c false
   */
  bool dictionarySeenBefore(const int& index);

  /**
   * Updates the current dictionary to that given by the index. Updates both
   * the current dictionary in the library and that in the system settings
   * window.
   *
   * @param index the position of the dictionary in the dictionary list or
   * combo box in the settings window
   */
  void updateDictionary(const int& index);

  /**
   * Convenience function to update the current dictionary based on a
   * locale layout.
   *
   * @param layout the locale on ISO 639/ISO 3166 standard form, that is:
   * @c language[_COUNTRY]
   */
  void updateDictionary(const QString& layout);

  /**
   * Updates the System Tray menu. For instance, if the worker thread is
   * stopped we want to change the top action from "Stop" to "Run".
   *
   * @param active set to @c true to ensure that updating to active state and
   * @c false to inactive state independent of the state of the worker thread
   *
   * @note this function will rebuild the menu completely as it is much easier
   * then having to remove and insert menu items at certain locations
   */
  void updateSystemTrayIconMenu(bool active = false);

  /**
   * Updates the tool button's menu pop-up with new dictionary information.
   * If the dictionary has not been used before it's added to the menu.
   */
  void updateToolButtonMenu();

  /**
   * Shows which command line options are available for the application.
   */
  void showCommandLineHelp();

  protected:
  /**
   * Adds a dictionary given by the index @p dictionaryIndex to the tool
   * button's pop-up menu. An country flag icon is added if available in the
   * settings dictionary combo box.
   *
   * @param dictionaryIndex the index used to locate the dictionary
   * @param icon the icon to use
   */
  void addDictionaryToMenu(const int& dictionaryIndex, const QIcon& icon = QIcon());

  /**
   * Saves the current application settings and state if requested by the
   * sessions manager. The settings are written to the directory and file
   * specified by the organization, application name and session ID, which by
   * default is @c "~/.config/kisa/kisa_SESIONID.conf"
   *
   * @note This method always uses the QSettings::IniFormat.
   *
   * @note This differs from KisaSettings::writeSettings() as it also stores
   * the used dictionaries (in reveres order) under the sessions group. These
   * settings can only be written and read from here.
   *
   * @note Using this method over QApplication::saveState() even though the
   * latter is used when answering to local calls from the sessions manager.
   * This method does not get called on application start when the session
   * manager tries to determine if the application is session aware. Hopefully
   * this will reduce the amount of residual session files left, and there
   * will be no need for any maintenance cleanup.
   *
   * @param manager the desktop session manager
   *
   * @see restoreSession()
   */
  void commitData(QSessionManager& manager);

  /**
   * Reimplementation of the the main event filter.
   *
   * Filters QEvent::KeyboardLayoutChange events for the main widget window,
   * so it knows when to update the current dictionary. If the event has been
   * handled the function will return @c return true and stop it from
   * being handled further, otherwise @c false is returned.
   *
   * @param object pointer to object that generated the event
   * @param event pointer to the event object
   * @return true if the event was handled, @c false otherwise
   */
  bool eventFilter(QObject *object, QEvent *event);

  /**
   * Reads the current application settings and state when restoring from a
   * previous session. The settings are read from the directory and file
   * specified by the organization, application name and session ID, which by
   * default is @c "~/.config/Kisa/Kisa_SESSIONID.conf"
   *
   * The state consists of the used dictionaries.
   *
   * @note This method always uses the QSettings::IniFormat.
   *
   * @see commitData()
   */
  void restoreSession();

  /**
   * Gets the current keyboard layout and updates the dictionary accordingly
   * based on the locale.
   *
   * @see KisaLib::getKeyboardInputLayout()
   */
  void updateOnNewKeyboardLayout();

  /**
   * Updates the text direction for the text field to the current keyboard
   * input layout direction and locale.
   */
  void updateTextDirection();

  /**
   * Updates the icon or text showing the current dictionary in use. If an
   * icon is set to be used and found, it will be displayed and returned. If
   * not, the language code of the current dictionary will be displayed.
   *
   * @return the current country flag icon, or none if no icon is found
   */
  QIcon updateToolButtonIcon();

  /**
   * Updates to either display all the icon flags in the dictionary history menu
   * or to hide them based on KisaSettings::showFlag() setting.
   */
  void updateToolButtonIconHistory();

  public slots:
  /**
   * Convenience slot to call the static versions of aboutQt. The
   * QApplication::aboutQt slot closes the program for some reason.
   *
   * @image html kisa_about_qt.png "The current Qt information"
   */
  void aboutQt();

  /**
   * Adds a word character to the text field. If the character is a backspace,
   * '\\b', then the last character is removed from the word. If
   * the character is a space character then spell checking is performed using
   * checkWord().
   *
   * @note Space characters and punctuation, such as space, enter, tab, ".",
   * ",", "!" and so on will trigger a spell check on the word. Any character
   * other then a word character, including hyphen, '-', will be ignored and
   * erases the word.
   *
   * @param character the character to add to the word
   */
  void addCharacter(const QChar& character);

  /**
   * Adds the current word in the text field to the personal word list.
   *
   */
  void addToPersonalWordList();

  /**
   * Spell checks the word @p word by passing it to the spell checker. Used
   * whenever there is a new word available, that is, when input is
   * completed by a space or punctuation character.
   *
   * @param word the word to check
   *
   * @see addCharacter()
   */
  void checkWord(const QString& word);

  /**
   * Convenience slot to clear and reset the mouse pointer. Used by the
   * kisaTimer when application has been idle too long.
   */
  void clear();

  /**
   * Shows customized context menu for the text field that has action icons
   * (added by createLineEditContextMenu()) and also the custom command name
   * from the settings menu.
   *
   * The slot is called when the QWidget::customContextMenuRequested() signal
   * is emitted, that is, when right-clicking on the text field.
   *
   * @note Reimplementing the standard context menu for QLineEdit stopped
   * working for some reason.
   *
   * @image html kisa_context_menu.png "Kisa's custom context menu"
   *
   * @param point used to obtain the position where the mouse cursor
   * was when the event was generated
   */
  void contextMenuEvent(const QPoint& point);

  /**
   * Convenience slot to open the external Kisa help page.
   */
  void getHelp();

  /**
   * Ignores the current word in the text field by adding it to the sessions
   * list. Subsequent occurrences of this word will be ignored by the
   * spell checker for the reminder of the session.
   */
  void ignoreWord();

  /**
   * Convenience slot to removes all the seen dictionaries from the dictionary
   * history and add the default "clear history" command. After calling this
   * slot the tool button pop-up menu will be reset and contain no
   * dictionaries except the one currently used.
   */
  void resetToolButtonMenu();

  /**
   * Restarts the worker thread, but only if it is running.
   */
  void restartThread();

  /**
   * Slot that performs variable substitution and runs the custom command
   * given in the settings as a detached process.
   *
   * The command can be any known system command or a protocol like @c http.
   * The command has to be either given by its full path or the path has to be
   * in the @c $PATH system variable.
   *
   * The Desktop's settings determine which application is used when executing
   * the protocol.
   *
   * @image html kisa_context_menu.png "The custom command in the context menu pop-up"
   *
   * Available variable substitutions are:
   *   @li @c $LANG is substituted with the current dictionary language
   *   @li @c $WORD is substituted with the current word in the text field
   *
   * @see KisaSettings::cusomCommand()
   */
  void runCustomCommand();

  /**
   * Shows information about the application whenever the user selects "About"
   * from the system tray icon menu.
   *
   * @image html kisa_about.png "The current application information"
   */
  void showAbout();

  /**
   * Starts the worker thread if it's not running. When the thread is running
   * the main window widget is enabled.
   *
   * @param updateIconMenu if @c true the icon menu will be updated, otherwise
   * it's left alone, default is @c true
   *
   * @image html kisa_enabled.png "Enabled main window widget"
   */
  void startThread(const bool updateIconMenu = true);

  /**
   * Stops the worker thread if it's not running. When the thread is stopped
   * the main window widget is disabled.
   *
   * @param updateIconMenu if @c true the icon menu will be updated, otherwise
   * it's left alone, default is @c true
   *
   * @image html kisa_disabled.png "Disabled main window widget"
   */
  void stopThread(const bool updateIconMenu = true);

  /**
   * Called whenever the tool button is clicked and switches to the next seen
   * dictionary. If only one dictionary has been used so far, no event occurs.
   *
   * @image html kisa_dictionary_popup.png "Kisa main widget with a pop-up showing the recently used dictionaries"
   */
  void switchToNextDictionary();

  /**
   * Called whenever the system tray icon has been activated, for example
   * when the user clicks on the system tray icon.
   *
   * @image html systray_menu.png "Kisa system tray menu"
   *
   * @param aReason how the system tray was activated
   */
  void systemTrayIconActivated(QSystemTrayIcon::ActivationReason aReason);

  /**
   * Toggles the worker thread. That is, if the thread is running it will be
   * stopped and if not it will be started.
   *
   * @param updateIconMenu if @c true the icon menu will be updated, otherwise
   * it's left alone, default is @c true
   */
  void toggleThread(const bool updateIconMenu = true);

  /**
   * Used when an action from the tool button menu is selected. For instance,
   * if the users changes dictionary or clears the used dictionaries history.
   *
   * @param action the action selected by the user
   */
  void toolButtonMenuActivated(QAction* action);

  /**
   * Convenience slot that is called whenever the text in the text field
   * is changed. For instance, when replaced by a suggestion from the pop-up
   * menu.
   *
   * @note If the option "add to clipboard" has been selected the, the text in
   * @p newText is added to the clipboard.
   *
   * @note If the option "replace in client application" has been selected the
   * text in @p newText is sent back to the client application.
   *
   * @param newText the new word to use when replacing the old text with
   */
  void updateText(const QString& newText);

  /**
   * Called whenever the settings in the settings window have been changed.
   *
   * Updates, the main window hint ("stay on top"), the font, the current
   * dictionary used and the "show country flag" and "custom command" options.
   */
  void updateSettings();

  private:

  /**
   * Main initialization function that calls all the other initialization
   * functions.
   *
   * @param display the display command line argument
   * @param language the language command line argument
   * @param iconsPath the icons path command line argument
   * @param flagsPath the flags path command line argument
   * @param dictDir the dictionary directory command line argument
   * @param aspellPrefix the Aspell prefix command line argument
   * @param extraDicts the extra dictionaries command line argument
   */
  void init(const QString& display,
            const QString& language,
            const QString& iconsPath,
            const QString& flagsPath,
            const QString& dictDir,
            const QString& aspellPrefix,
            const QString& extraDicts);

  /**
   * Initializes the library.
   *
   * @param display the display command line argument
   * @param iconsPath the icons path command line argument
   * @param flagsPath the flags path command line argument
   * @param dictionary the default dictionary to use in ISO 639 and ISO 3166
   * format
   * @param dictDir the dictionary directory command line argument
   * @param aspellPrefix the Aspell prefix command line argument
   * @param extraDicts the extra dictionaries command line argument
   *
   * @see init()
   */
  void initLibrary(const QString& display,
                   const QString& iconsPath,
                   const QString& flagsPath,
                   const QString& dictionary,
                   const QString& dictDir,
                   const QString& aspellPrefix,
                   const QString& extraDicts);

  /**
   * Initializes all the member variables except those that have separate
   * functions, such as the kisaSettings, kisaThread, kisaTimer and so on.
   *
   * @see init()
   */
  void initMembers();


  /**
   * Initializes any saved session information from a previous run. If none is
   * found then the settings are loaded from the saved settings file.
   *
   * @see readSettings()
   * @see init()
   */
  void initSession();

  /**
   * Initializes all signals and slots by, yes, making all the needed
   * connections.
   */
  void initSignalsAndSlots();

  /**
   * Initializes the system tray icon.
   *
   * @param kisaIcon the icon to uses in the system tray
   */
  void initSystemTray(const QIcon& kisaIcon);

  /**
   * Initializes the widget which is the main window of the application.
   *
   * @param kisaIcon the application icon to use
   */
  void initWidget(const QIcon& kisaIcon);

  /**
   * Checks if a given character @p key is valid for output. That is, if the
   * key is a letter, hyphen (and hyphen is not set as a punctuation) or an
   * apostrophe then the character key can be show in the main widget.
   *
   * @param key the character key to validate
   * @return @c true if the key is valid based on the validation above, @c false
   * otherwise
   *
   * @todo add more validation rules here
   */
  bool isValidForOutput(const QChar& key);

  /**
   * Checks if a given word string @p word is valid for output. That is, if
   * all the characters in the string are valid by being either a letter, hyphen
   * (and hyphen is not set as a punctuation) or an apostrophe then the word
   * string can be show in the main widget.
   *
   * @param word the word string to validate
   * @return @c true if the word string is valid, @c false otherwise
   */
  bool isValidForOutput(const QString& word);

  /**
   * Checks if a given character @p should be treated as punctuation. That is,
   * if the character is a space, hyphen (and hyphen is set as a punctuation) or
   * other punctuation.
   *
   * @param key the character to check
   * @return @c true if the key is valid based on the validation above, @c false
   * otherwise
   */
  bool isValidPunctuation(const QChar& key);

  /**
   * Loads any possible translation for the given locale @p locale. If none is
   * given the current system locale will be used.
   */
  void loadTranslations(QString locale = QString());

  /**
   * Parses the command line arguments and stores them in the given function
   * parameter pointers.
   *
   * @param argc the command line argument count
   * @param argv the actual command line arguments
   * @param display the display command line argument
   * @param language the language command line argument
   * @param iconsPath the icons path command line argument
   * @param flagsPath the flags path command line argument
   * @param dictDir the dictionary directory command line argument
   * @param aspellPrefix the Aspell prefix command line argument
   * @param extraDicts the extra dictionaries command line argument
   *
   * @note this function will change all but argc and argv values if a
   * corresponding command line function is found
   *
   * @see init()
   */
  void parseInputArguments(int& argc, char** argv, QString& display,
                           QString& language, QString& iconsPath,
                           QString& flagsPath, QString& dictDir,
                           QString& aspellPrefix, QString& extraDicts);

  /**
   * Pointer to the library object.
   */
  KisaLib* kisaLib;
  /**
   * Pointer to completer that is used to display the suggestions pop-up.
   */
  QCompleter* completer;

  /**
   * Convenience variable that contains the setting given by
   * KisaSettings::hyphenAsPunctuation() and stored here to reduce function
   * calls in Kisa::addCharacter() (which is called at each keystroke).
   *
   * @see KisaSettings::hyphenAsPunctuation()
   */
  bool hyphenAsPunctuation;

  /**
   * Pointer to the settings window.
   */
  KisaSettings* kisaSettings;

  /**
   * Pointer to the main worker thread.
   */
  KisaThread* kisaThread;

  /**
   * Timer that clears the text field if idle for 20 s.
   */
  QTimer* kisaTimer;

  /**
   * Pointer to the applications system tray entry.
   */
  QSystemTrayIcon* kisaTrayIcon;

  /**
   * Pointer to the main window.
   */
  KisaWidget* kisaWidget;

  /**
   * Pointer to the tool button menu containing the used dictionaries.
   */
  QMenu* kisaToolButtonMenu;

  /**
   * Pointer to the system tray icon menu containing all the possible actions.
   */
  QMenu* kisaTrayIconMenu;

  /**
   * Pointer to the translator.
   *
   * @note that the translator must be created before the application's widgets
   */
  QTranslator* kisaTranslator;

  /**
   * Flag that if set to @c true the current word has been spell checked,
   * otherwise not.
   */
  bool wordIsChecked;

  /**
   * Word input buffer. Contains the "raw" inputed word and not just the
   * displayed characters shown in KisaWidget::lineEdit. Used when replacing
   * the word in the client window as wee needed everything else after the word
   * as well.
   */
  QString wordBuffer;
};
#endif
